﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace SequentialPattern.BasicClass
{
    /// <summary>
    /// 序列: 由一群(List)項目集(ItemSet)所組成先後交易順序
    /// </summary>
    [Serializable]
    public class Sequence : Collection<ItemSet>
    {
        /// <summary>
        /// 序列的長度
        /// </summary>
        public int Length
        {
            get { return this.Count; }
        }

        /// <summary>
        /// 建構子
        /// </summary>
        public Sequence() { }

        /// <summary>
        /// 建構子, 並初始化序列
        /// </summary>
        /// <param name="sequence">初始序列</param>
        public Sequence(Sequence sequence)
        {
            foreach (ItemSet itemSet in sequence)
                this.Add(new ItemSet(itemSet));
        }

        /// <summary>
        /// 依據某個交易時間將item加入
        /// </summary>
        /// <param name="timeId"></param>
        /// <param name="item"></param>
        public void Add(int timeId, Item item)
        {
            List<ItemSet> set = (from itemSet in this where itemSet.TimeID == timeId select itemSet).ToList<ItemSet>();
            if (set.Count == 0)
            {
                ItemSet tmpSet = new ItemSet();
                tmpSet.TimeID = timeId;
                tmpSet.Add(item);
                this.Add(tmpSet);
            }
            else
            {
                set[0].Add(item);
            }
        }

        /// <summary>
        /// 檢查是否為其他序列的子序列
        /// </summary>
        /// <param name="sequence">輸入序列</param>
        /// <returns>輸出是否為輸入序列的子序列</returns>
        public bool IsSubSequence(Sequence sequence)
        {
            // 項目集總數比目前序列項目集總數還小的話, 必不為輸入序列之子序列
            int idxThis, idxSeq;
            GetComparedIndex(sequence, out idxThis, out idxSeq);

            if (idxThis == -1 || idxSeq == -1) return false; // 若有null則代表不為子序列
            return idxThis == this.Count; // 若idxThis == this.Count代表序列有比完, 為子序列
        }

        /// <summary>
        /// 取得比對成功後, 剩餘序列的起始所引
        /// </summary>
        /// <param name="sequence">輸入序列</param>
        /// <param name="indexThis">比對後本序列之索引</param>
        /// <param name="indexSeq">比對後目標序列之索引</param>
        public void GetComparedIndex(Sequence sequence, out int indexThis, out int indexSeq)
        {
            if (sequence.Count < this.Count)
            {
                indexThis = -1;
                indexSeq = -1;
                return;
            }

            int idxThis = 0, idxSeq = 0;

            while (idxThis < this.Count && idxSeq < sequence.Count)
            {
                /*
                 * 比較
                 */
                if (sequence[idxSeq].Contains(this[idxThis]))
                { ++idxThis; ++idxSeq; }
                else
                    ++idxSeq;
            }

            indexThis = idxThis;
            indexSeq = idxSeq;
        }

        /// <summary>
        /// 取得目前序列比對輸入序列後剩餘的序列
        /// </summary>
        /// <param name="sequence">輸入序列</param>
        /// <returns>回傳後段剩餘的序列</returns>
        /// <example>
        /// 序列{(10)(20)}為輸入序列{(10)(30)(20)(50)(20)}的子序列,則回傳{(50)(20)}
        /// 若目前序列不為輸入序列的子序列則回傳null
        /// </example>
        public Sequence GetComparedAfterSequence(Sequence sequence)
        {
            // 項目集總數比目前序列項目集總數還小的話, 必不為輸入序列之子序列
            if (sequence.Count < this.Count) return null;

            int idxThis, idxSeq;
            GetComparedIndex(sequence, out idxThis, out idxSeq);

            if (idxThis == -1 || idxSeq == -1) return null; // 必不為子序列, 回傳null

            // 將sequence中idxSeq後的ItemSet加入result中
            Sequence result = new Sequence();

            for (; idxSeq < sequence.Count; ++idxSeq)
                result.Add(sequence[idxSeq]);

            return result;
        }

        /// <summary>
        /// 將序列轉成字串
        /// </summary>
        /// <returns>序列字串</returns>
        public override string ToString()
        {
            string result = "<";
            foreach (ItemSet IS in this)
                result += IS.ToString();
            result += ">";
            return result;
        }

        /// <summary>
        /// 將序列依照時間排序
        /// </summary>
        public void Sort()
        {
            // this=this.OrderBy<this.
            IEnumerable<ItemSet> iSet = this.OrderBy<ItemSet, int>(itemSet=>itemSet.TimeID);
            int i = 0;
            foreach (ItemSet set in iSet)
            {
                set.Sort();
                this[i++] = set;
            }
        }
    }
}
